#Using R markdown
##Working with R Markdown
This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.
Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Cmd+Shift+Enter.
print("Hello")
[1] "Hello"
plot(cars)

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Cmd+Option+I.
When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Cmd+Shift+K to preview the HTML file).
The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.
Installing the robis package
First install the robis package. If the package is already installed, make sure the version is 2.10.0 or higher.
if (!"robis" %in% rownames(installed.packages())) {
remotes::install_github("iobis/robis")
} else if (packageVersion("robis") < "2.10.0") {
knitr::knit_exit()
}
We will also need the following packages:
library(robis)
library(dplyr)
library(ggplot2)
library(knitr)
library(ggtree)
library(mapview)
library(sf)
library(RColorBrewer)
eDNA data access in OBIS
In this notebook we will explore data published to OBIS using the new DNADerivedData extension. This notebook is available at https://iobis.github.io/notebook-dnaderiveddata/
Finding sequence based data
Before we fetch any occurrence data let’s first find out which datasets in OBIS are using the DNADerivedData extension. The dataset() function takes many of the same parameters as the occurrence() function, including hasextensions which allows us to limit our search to occurrences which have linked extension records of a specific type. Possible values include MeasurementOrFact and DNADerivedData.
dna_datasets <- dataset(hasextensions = "DNADerivedData")
Retrieved 51 records of approximately 51 (100%)
any(sapply(dna_datasets_geo, is.null))
[1] FALSE
#In case of error try setting: #mapviewOptions(fgb = FALSE)
Fetching sequence based data
Let’s fetch all occurrences from the PacMAN dataset by using the datasetid parameter. Alternatively, set hasextensions to DNADerivedData. Also set the extensions parameter to ensure that the nested extension records are included in the results.
pacmanID<-dna_datasets %>% filter(grepl("PacMAN", title))%>%
select(id)
occ <- occurrence(datasetid = pacmanID, extensions = "DNADerivedData", wrims = T)
Retrieved 5000 records of approximately 8612 (58%)
Retrieved 8612 records of approximately 8612 (100%)
occ
Let’s take a look at the taxonomic composition of our dataset:
occ %>%
group_by(phylum) %>%
summarize(species = length(unique(speciesid)), records = n()) %>%
arrange(desc(species))
Working with DNADerivedData records
This is a quick demo of working with sequences from the DNADerivedData extension. For the remainder of the notebook I will work with a subset of the sequence data.
dna_subset <- dna %>%
filter(phylum == "Chordata" | phylum == "Mollusca" & !is.na(species)) %>%
head(100)
To be able to make use of existing bioinformatics packages we first need to convert our DNA_sequence column to a DNAbin object. This can be done using as.DNAbin function from the ape (Analyses of Phylogenetics and Evolution) package.
dna_subset_COI <- dna_subset %>% filter(target_gene =="COI")
sequences <- sapply(strsplit(as.character(dna_subset_COI$DNA_sequence), ""), tolower)
names(sequences) <- dna_subset_COI$id
sequences <- ape::as.DNAbin(sequences)
sequences
73 DNA sequences in binary format stored in a list.
Mean sequence length: 300.397
Shortest sequence: 200
Longest sequence: 313
Labels:
00197f78-a410-4bd0-bbf9-375d252d5033
001e89fb-008a-482f-89e6-f226d72750d6
002b7cf9-5c46-493c-b87c-437b07b77c1e
002c4bad-af82-422e-86fc-55fe37913d45
008a69f0-8fdd-47b0-9968-b203992442de
008e1ab9-55be-4372-9196-892533ee1836
...
Base composition:
a c g t
0.222 0.184 0.190 0.404
(Total: 21.93 kb)
Now we can align the sequences using MAFFT. The ips (Interfaces to Phylogenetic Software in R) package provides an interface to the MAFFT software.
aligned <- ips::mafft(sequences, method = "auto")
aligned
73 DNA sequences in binary format stored in a matrix.
All sequences of same length: 316
Labels:
00197f78-a410-4bd0-bbf9-375d252d5033
001e89fb-008a-482f-89e6-f226d72750d6
002b7cf9-5c46-493c-b87c-437b07b77c1e
002c4bad-af82-422e-86fc-55fe37913d45
008a69f0-8fdd-47b0-9968-b203992442de
008e1ab9-55be-4372-9196-892533ee1836
...
Base composition:
a c g t
0.222 0.184 0.190 0.404
(Total: 23.07 kb)
Let’s visualize the aligned sequences:
ape::image.DNAbin(aligned, col=c(RColorBrewer::brewer.pal(6, "Pastel1")), show.bases=T, show.labels=F, base.cex=0.4)

Finally, we can calculate a distance matrix and construct a tree using dist.dna and njs.
distances <- ape::dist.gene(aligned)
tree <- ape::njs(distances) %>%
left_join(dna_subset %>% select(id, year = date_year, species), by = c("label" = "id"))
ggtree(tree, layout = "circular", cex = 0.3) +
geom_tiplab(size = 2, aes(label = species, color = year)) +
scale_color_viridis_c(option = "magma", end = 0.9)

Using the mapper
DNADerivedData extension records can also be included in data downloads from the mapper. To do so, tick the DNADerivedData checkbox in the download confirmation form.
The resulting archive will include CSV files for the occurrences as well as for the selected extensions. The occurrence and extension tables can be joined using the id column.
LS0tCnRpdGxlOiAiRE5BRGVyaXZlZERhdGEgZXh0ZW5zaW9uIGRhdGEgYWNjZXNzIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCmF1dGhvcjogIlBpZXRlciBQcm92b29zdCwgU2FhcmEgU3VvbWluZW4iCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiNVc2luZyBSIG1hcmtkb3duCgojI1dvcmtpbmcgd2l0aCBSIE1hcmtkb3duCgpUaGlzIGlzIGFuIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBOb3RlYm9vay4gV2hlbiB5b3UgZXhlY3V0ZSBjb2RlIHdpdGhpbiB0aGUgbm90ZWJvb2ssIHRoZSByZXN1bHRzIGFwcGVhciBiZW5lYXRoIHRoZSBjb2RlLiAKClRyeSBleGVjdXRpbmcgdGhpcyBjaHVuayBieSBjbGlja2luZyB0aGUgKlJ1biogYnV0dG9uIHdpdGhpbiB0aGUgY2h1bmsgb3IgYnkgcGxhY2luZyB5b3VyIGN1cnNvciBpbnNpZGUgaXQgYW5kIHByZXNzaW5nICpDbWQrU2hpZnQrRW50ZXIqLiAKCmBgYHtyfQpwbG90KGNhcnMpCmBgYAoKQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkNtZCtPcHRpb24rSSouCgpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkNtZCtTaGlmdCtLKiB0byBwcmV2aWV3IHRoZSBIVE1MIGZpbGUpLiAKClRoZSBwcmV2aWV3IHNob3dzIHlvdSBhIHJlbmRlcmVkIEhUTUwgY29weSBvZiB0aGUgY29udGVudHMgb2YgdGhlIGVkaXRvci4gQ29uc2VxdWVudGx5LCB1bmxpa2UgKktuaXQqLCAqUHJldmlldyogZG9lcyBub3QgcnVuIGFueSBSIGNvZGUgY2h1bmtzLiBJbnN0ZWFkLCB0aGUgb3V0cHV0IG9mIHRoZSBjaHVuayB3aGVuIGl0IHdhcyBsYXN0IHJ1biBpbiB0aGUgZWRpdG9yIGlzIGRpc3BsYXllZC4KCgojIyBJbnN0YWxsaW5nIHRoZSByb2JpcyBwYWNrYWdlCgpGaXJzdCBpbnN0YWxsIHRoZSBbcm9iaXMgcGFja2FnZV0oaHR0cHM6Ly9naXRodWIuY29tL2lvYmlzL3JvYmlzKS4gSWYgdGhlIHBhY2thZ2UgaXMgYWxyZWFkeSBpbnN0YWxsZWQsIG1ha2Ugc3VyZSB0aGUgdmVyc2lvbiBpcyAyLjEwLjAgb3IgaGlnaGVyLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmlmICghInJvYmlzIiAlaW4lIHJvd25hbWVzKGluc3RhbGxlZC5wYWNrYWdlcygpKSkgewogIHJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJpb2Jpcy9yb2JpcyIpCn0gZWxzZSBpZiAocGFja2FnZVZlcnNpb24oInJvYmlzIikgPCAiMi4xMC4wIikgewogIGtuaXRyOjprbml0X2V4aXQoKQp9CgpgYGAKCldlIHdpbGwgYWxzbyBuZWVkIHRoZSBmb2xsb3dpbmcgcGFja2FnZXM6CgoKYGBge3J9CmxpYnJhcnkocm9iaXMpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShrbml0cikKbGlicmFyeShnZ3RyZWUpCmxpYnJhcnkobWFwdmlldykKbGlicmFyeShzZikKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmBgYAoKCgojIGVETkEgZGF0YSBhY2Nlc3MgaW4gT0JJUwoKSW4gdGhpcyBub3RlYm9vayB3ZSB3aWxsIGV4cGxvcmUgZGF0YSBwdWJsaXNoZWQgdG8gT0JJUyB1c2luZyB0aGUgbmV3IFtETkFEZXJpdmVkRGF0YSBleHRlbnNpb25dKGh0dHBzOi8vcnMuZ2JpZi5vcmcvZXh0ZW5zaW9uL2diaWYvMS4wL2RuYV9kZXJpdmVkX2RhdGFfMjAyMS0wNy0wNS54bWwpLiBUaGlzIG5vdGVib29rIGlzIGF2YWlsYWJsZSBhdCBbaHR0cHM6Ly9pb2Jpcy5naXRodWIuaW8vbm90ZWJvb2stZG5hZGVyaXZlZGRhdGEvXShodHRwczovL2lvYmlzLmdpdGh1Yi5pby9ub3RlYm9vay1kbmFkZXJpdmVkZGF0YS8pCgoKIyMgRmluZGluZyBzZXF1ZW5jZSBiYXNlZCBkYXRhCgpCZWZvcmUgd2UgZmV0Y2ggYW55IG9jY3VycmVuY2UgZGF0YSBsZXQncyBmaXJzdCBmaW5kIG91dCB3aGljaCBkYXRhc2V0cyBpbiBPQklTIGFyZSB1c2luZyB0aGUgRE5BRGVyaXZlZERhdGEgZXh0ZW5zaW9uLiBUaGUgYGRhdGFzZXQoKWAgZnVuY3Rpb24gdGFrZXMgbWFueSBvZiB0aGUgc2FtZSBwYXJhbWV0ZXJzIGFzIHRoZSBgb2NjdXJyZW5jZSgpYCBmdW5jdGlvbiwgaW5jbHVkaW5nIGBoYXNleHRlbnNpb25zYCB3aGljaCBhbGxvd3MgdXMgdG8gbGltaXQgb3VyIHNlYXJjaCB0byBvY2N1cnJlbmNlcyB3aGljaCBoYXZlIGxpbmtlZCBleHRlbnNpb24gcmVjb3JkcyBvZiBhIHNwZWNpZmljIHR5cGUuIFBvc3NpYmxlIHZhbHVlcyBpbmNsdWRlIGBNZWFzdXJlbWVudE9yRmFjdGAgYW5kIGBETkFEZXJpdmVkRGF0YWAuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkbmFfZGF0YXNldHMgPC0gZGF0YXNldChoYXNleHRlbnNpb25zID0gIkROQURlcml2ZWREYXRhIikKCmRuYV9kYXRhc2V0cyAlPiUKICBzZWxlY3QodXJsLCB0aXRsZSwgcmVjb3JkcykgJT4lCiAga2FibGUoKQpgYGAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKZG5hX2RhdGFzZXRzX2dlbzwtZG5hX2RhdGFzZXRzJGV4dGVudFshaXMubmEoZG5hX2RhdGFzZXRzJGV4dGVudCldCgplZG5hX3BvbHlnb25zIDwtIHN0X2FzX3NmYyhkbmFfZGF0YXNldHNfZ2VvLCBjcnMgPSA0MzI2KQoKbWFwdmlldyhlZG5hX3BvbHlnb25zKQoKYGBgCiNJbiBjYXNlIG9mIGVycm9yIHRyeSBzZXR0aW5nOiAKI21hcHZpZXdPcHRpb25zKGZnYiA9IEZBTFNFKQoKCgojIyBGZXRjaGluZyBzZXF1ZW5jZSBiYXNlZCBkYXRhCgpMZXQncyBmZXRjaCBhbGwgb2NjdXJyZW5jZXMgZnJvbSB0aGUgUGFjTUFOIGRhdGFzZXQgYnkgdXNpbmcgdGhlIGBkYXRhc2V0aWRgIHBhcmFtZXRlci4gQWx0ZXJuYXRpdmVseSwgc2V0IGBoYXNleHRlbnNpb25zYCB0byBgRE5BRGVyaXZlZERhdGFgLiBBbHNvIHNldCB0aGUgYGV4dGVuc2lvbnNgIHBhcmFtZXRlciB0byBlbnN1cmUgdGhhdCB0aGUgbmVzdGVkIGV4dGVuc2lvbiByZWNvcmRzIGFyZSBpbmNsdWRlZCBpbiB0aGUgcmVzdWx0cy4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwYWNtYW5JRDwtZG5hX2RhdGFzZXRzICU+JSBmaWx0ZXIoZ3JlcGwoIlBhY01BTiIsIHRpdGxlKSklPiUKc2VsZWN0KGlkKQoKb2NjIDwtIG9jY3VycmVuY2UoZGF0YXNldGlkID0gcGFjbWFuSUQsIGV4dGVuc2lvbnMgPSAiRE5BRGVyaXZlZERhdGEiLCB3cmltcyA9IFQpCm9jYwpgYGAKCkxldCdzIHRha2UgYSBsb29rIGF0IHRoZSB0YXhvbm9taWMgY29tcG9zaXRpb24gb2Ygb3VyIGRhdGFzZXQ6CgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kb2NjICU+JQogIGdyb3VwX2J5KHBoeWx1bSkgJT4lCiAgc3VtbWFyaXplKHNwZWNpZXMgPSBsZW5ndGgodW5pcXVlKHNwZWNpZXNpZCkpLCByZWNvcmRzID0gbigpKSAlPiUKICBhcnJhbmdlKGRlc2Moc3BlY2llcykpCmBgYAoKIyMgRXh0cmFjdGluZyBETkFEZXJpdmVkRGF0YSByZWNvcmRzCgpUaGUgRE5BRGVyaXZlZERhdGEgcmVjb3JkcyBhcmUgY3VycmVudGx5IG5lc3RlZCB3aXRoaW4gdGhlIG9jY3VycmVuY2VzIHdlIGZldGNoZWQgZnJvbSBPQklTLiBUbyBleHRyYWN0IHRoZSBleHRlbnNpb24gcmVjb3JkcyB3ZSBjYW4gdXNlIHRoZSBgdW5uZXN0X2V4dGVuc2lvbigpYCBmdW5jdGlvbi4gUGFzcyBhbnkgZmllbGRzIGZyb20gdGhlIG9jY3VycmVuY2UgdGFibGUgeW91IHdvdWxkIGxpa2UgdG8ga2VlcCB1c2luZyB0aGUgYGZpZWxkc2AgcGFyYW1ldGVyLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmRuYSA8LSB1bm5lc3RfZXh0ZW5zaW9uKG9jYywgIkROQURlcml2ZWREYXRhIiwgZmllbGRzID0gYygiaWQiLCAicGh5bHVtIiwgImNsYXNzIiwgIm9yZGVyIiwgImZhbWlseSIsICJnZW51cyIsICJzcGVjaWVzIiwgImRhdGVfeWVhciIpKQpkbmEgJT4lIHNlbGVjdCh3aGVyZSh+c3VtKCFpcy5uYSguKSkgPiAwKSkKYGBgCgpMZXQncyB0YWtlIGEgbG9vayBhdCBzb21lIHByb3BlcnRpZXMgb2YgdGhlIHNlcXVlbmNlIHJlY29yZHMuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZG5hICU+JQogIGdyb3VwX2J5KHRhcmdldF9nZW5lLCBwY3JfcHJpbWVyX25hbWVfZm9yd2FyZCwgcGNyX3ByaW1lcl9uYW1lX3JldmVyc2UpICU+JQogIHN1bW1hcml6ZShyZWNvcmRzID0gbigpKSAlPiUKICBrYWJsZSgpCmBgYAoKIyMgV29ya2luZyB3aXRoIEROQURlcml2ZWREYXRhIHJlY29yZHMKClRoaXMgaXMgYSBxdWljayBkZW1vIG9mIHdvcmtpbmcgd2l0aCBzZXF1ZW5jZXMgZnJvbSB0aGUgRE5BRGVyaXZlZERhdGEgZXh0ZW5zaW9uLiBGb3IgdGhlIHJlbWFpbmRlciBvZiB0aGUgbm90ZWJvb2sgSSB3aWxsIHdvcmsgd2l0aCBhIHN1YnNldCBvZiB0aGUgc2VxdWVuY2UgZGF0YS4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkbmFfc3Vic2V0IDwtIGRuYSAlPiUKICBmaWx0ZXIocGh5bHVtID09ICJDaG9yZGF0YSIgJiAhaXMubmEoc3BlY2llcykpICU+JQogIGhlYWQoMTAwKQoKYGBgCgpUbyBiZSBhYmxlIHRvIG1ha2UgdXNlIG9mIGV4aXN0aW5nIGJpb2luZm9ybWF0aWNzIHBhY2thZ2VzIHdlIGZpcnN0IG5lZWQgdG8gY29udmVydCBvdXIgYEROQV9zZXF1ZW5jZWAgY29sdW1uIHRvIGEgYEROQWJpbmAgb2JqZWN0LiBUaGlzIGNhbiBiZSBkb25lIHVzaW5nIGBhcy5ETkFiaW5gIGZ1bmN0aW9uIGZyb20gdGhlIGBhcGVgIChBbmFseXNlcyBvZiBQaHlsb2dlbmV0aWNzIGFuZCBFdm9sdXRpb24pIHBhY2thZ2UuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKZG5hX3N1YnNldF9DT0kgPC0gZG5hX3N1YnNldCAlPiUgZmlsdGVyKHRhcmdldF9nZW5lID09IkNPSSIpCgpzZXF1ZW5jZXMgPC0gc2FwcGx5KHN0cnNwbGl0KGFzLmNoYXJhY3RlcihkbmFfc3Vic2V0X0NPSSRETkFfc2VxdWVuY2UpLCAiIiksIHRvbG93ZXIpCm5hbWVzKHNlcXVlbmNlcykgPC0gZG5hX3N1YnNldF9DT0kkaWQKc2VxdWVuY2VzIDwtIGFwZTo6YXMuRE5BYmluKHNlcXVlbmNlcykKc2VxdWVuY2VzCgpgYGAKCk5vdyB3ZSBjYW4gYWxpZ24gdGhlIHNlcXVlbmNlcyB1c2luZyBNQUZGVC4gVGhlIGBpcHNgIChJbnRlcmZhY2VzIHRvIFBoeWxvZ2VuZXRpYyBTb2Z0d2FyZSBpbiBSKSBwYWNrYWdlIHByb3ZpZGVzIGFuIGludGVyZmFjZSB0byB0aGUgTUFGRlQgc29mdHdhcmUuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQphbGlnbmVkIDwtIGlwczo6bWFmZnQoc2VxdWVuY2VzLCBtZXRob2QgPSAiYXV0byIpCmFsaWduZWQKYGBgCgpMZXQncyB2aXN1YWxpemUgdGhlIGFsaWduZWQgc2VxdWVuY2VzOgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KYXBlOjppbWFnZS5ETkFiaW4oYWxpZ25lZCwgY29sPWMoUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDYsICJQYXN0ZWwxIikpLCBzaG93LmJhc2VzPVQsIHNob3cubGFiZWxzPUYsIGJhc2UuY2V4PTAuNCkKYGBgCgpGaW5hbGx5LCB3ZSBjYW4gY2FsY3VsYXRlIGEgZGlzdGFuY2UgbWF0cml4IGFuZCBjb25zdHJ1Y3QgYSB0cmVlIHVzaW5nIGBkaXN0LmRuYWAgYW5kIGBuanNgLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZGlzdGFuY2VzIDwtIGFwZTo6ZGlzdC5nZW5lKGFsaWduZWQpCgp0cmVlIDwtIGFwZTo6bmpzKGRpc3RhbmNlcykgJT4lCiAgbGVmdF9qb2luKGRuYV9zdWJzZXQgJT4lIHNlbGVjdChpZCwgeWVhciA9IGRhdGVfeWVhciwgc3BlY2llcyksIGJ5ID0gYygibGFiZWwiID0gImlkIikpCgpnZ3RyZWUodHJlZSwgbGF5b3V0ID0gImNpcmN1bGFyIiwgY2V4ID0gMC4zKSArCiAgZ2VvbV90aXBsYWIoc2l6ZSA9IDIsIGFlcyhsYWJlbCA9IHNwZWNpZXMsIGNvbG9yID0geWVhcikpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2Mob3B0aW9uID0gIm1hZ21hIiwgZW5kID0gMC45KQoKYGBgCgojIFVzaW5nIHRoZSBtYXBwZXIKCkROQURlcml2ZWREYXRhIGV4dGVuc2lvbiByZWNvcmRzIGNhbiBhbHNvIGJlIGluY2x1ZGVkIGluIGRhdGEgZG93bmxvYWRzIGZyb20gdGhlIFttYXBwZXJdKGh0dHBzOi8vbWFwcGVyLm9iaXMub3JnLykuIFRvIGRvIHNvLCB0aWNrIHRoZSBETkFEZXJpdmVkRGF0YSBjaGVja2JveCBpbiB0aGUgZG93bmxvYWQgY29uZmlybWF0aW9uIGZvcm0uCgohW21hcHBlcl0oaW1hZ2VzL21hcHBlci5wbmcpCgpUaGUgcmVzdWx0aW5nIGFyY2hpdmUgd2lsbCBpbmNsdWRlIENTViBmaWxlcyBmb3IgdGhlIG9jY3VycmVuY2VzIGFzIHdlbGwgYXMgZm9yIHRoZSBzZWxlY3RlZCBleHRlbnNpb25zLiBUaGUgb2NjdXJyZW5jZSBhbmQgZXh0ZW5zaW9uIHRhYmxlcyBjYW4gYmUgam9pbmVkIHVzaW5nIHRoZSBgaWRgIGNvbHVtbi4KCiFbbWFwcGVyXShpbWFnZXMvZG93bmxvYWQucG5nKQoKCg==